﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace WindowsGame1.Model
{
    class Model
    {
        //Instanser
        Player _player = new Player();
        Level _level = new Level();
        List<Enemy> _enemy = new List<Enemy>();
        List<Carrot> _carrot = new List<Carrot>();
        List<EnemyMines> _enemyMines = new List<EnemyMines>();
        List<StalkingChicken> _stalkers = new List<StalkingChicken>();

        //fält
        const float _rightEdge = 1;
        bool        _hasCollidedWithGround = false;
        bool        _hasCollidedWithTrap = false;
        bool        _hasCollideWithBasket = false;
        bool        _hasCollideWithEnemy = false;
        bool        _enemyHasCollideWithGround = false;
        bool        _gotHurt = false;
        double      _hurtTimer = 0;
        List<int>   _activatedMine = new List<int>();
        bool        _playerWonLevel = false;
        bool        _playerAteCarrot = false;
        bool        _stalkerHurtBox = false;
        bool        _jumpedOnMine = false;

        public Model()
        {
        }

        internal void Update(float a_elapsedTime)
        {
            //PLAYER
            UpdatePlayer(a_elapsedTime);

            //ENEMIES
            UpdateEnemy(a_elapsedTime);

            UpdateStalker(a_elapsedTime);

            //KOLLISION MED KYCKLING-FIENDER
            for (int i = 0; i < _enemy.Count; i++)
            {
                if (IsCollidingWithEnemy(_enemy[i], _player))
                {
                    if (this._gotHurt != true)
                    {
                        if ((_enemy[i].GetPosition().Y - (_enemy[i]._sizes.Y / 2)) <= _player.GetPosition().Y)
                        {
                            _player.ReduceHealth(25);
                            this._gotHurt = true;
                        }
                        else if (_enemy[i].GetPosition().Y > _player.GetPosition().Y)
                        {
                            _enemy.Remove(_enemy.ElementAt(i));
                        }
                    }
                    _player.SetSpeed(0, -10);
                }
            }
            
            //KOLLISION MED MINOR
            for (int i = 0; i < _enemyMines.Count; i++)
            {
                if(IsCollidingWithMine(_enemyMines.ElementAt(i), _player))
                {
                    if (this._gotHurt != true)
                    {
                        if (_enemyMines[i].GetPosition().Y > _player.GetPosition().Y)
                        {
                            _player.ReduceHealth(50);

                            _player.SetSpeed(0, -10);

                            _enemyMines.Remove(_enemyMines.ElementAt(i));

                            _jumpedOnMine = true;
                        }
                    }
                }
            }
            
            //KOLLISION MED MORÖTTER
            for (int i = 0; i < _carrot.Count; i++)
            {
                if (IsCollidingWithCarrot(_carrot.ElementAt(i), _player))
                {
                    _playerAteCarrot = true;

                    _player.AddHealth(25);
                    //tar bort moroten om den nås
                    _carrot.Remove(_carrot.ElementAt(i));
                }
            }

            if (_gotHurt == true)
            {
                _hurtTimer += a_elapsedTime;
            }

            if (_hurtTimer > 1)
            {
                StopHurtTimer();
            }

        }

        internal void UpdatePlayer(float a_elapsedTime)
        {
            //Get the old position
            Vector2 oldPos = _player.GetPosition();

            //Get the new position
            _player.Update(a_elapsedTime);
            Vector2 newPos = _player.GetPosition();

            _hasCollidedWithGround = false;
            _hasCollidedWithTrap = false;
            _hasCollideWithBasket = false;
            Vector2 playerSpeed = _player.GetSpeed();
            Vector2 afterCollidedPos = Collide(oldPos, newPos, _player._sizes, ref playerSpeed, ref _hasCollidedWithGround);
            Vector2 afterCollidedTrapPos = CollideAtTrap(oldPos, newPos, _player._sizes, ref playerSpeed, ref _hasCollidedWithTrap);
            Vector2 afterCollidedBasketPos = CollideAtBasket(oldPos, newPos, _player._sizes, ref playerSpeed, ref _hasCollideWithBasket);

            //PLAYER
            //set the new speed and position after collision
            _player.SetPosition(afterCollidedPos.X, afterCollidedPos.Y);
            _player.SetSpeed(playerSpeed.X, playerSpeed.Y);
        }

        internal void UpdateEnemy(float a_elapsedTime)
        {
            Vector2 lastPosition;
            Vector2 newPosition;
            Vector2 EnemySpeed;
            Vector2 EnemyAfterCollidedPos;

            foreach (Enemy a in _enemy)
            {
                lastPosition = a.GetPosition();
                a.Update(a_elapsedTime, _level);
                newPosition = a.GetPosition();

                _enemyHasCollideWithGround = false;

                EnemySpeed = a.GetSpeed();
                EnemyAfterCollidedPos = Collide(lastPosition, newPosition, a._sizes, ref EnemySpeed, ref _enemyHasCollideWithGround);

                a.SetPosition(EnemyAfterCollidedPos.X, EnemyAfterCollidedPos.Y);
            }
        }

        internal void UpdateStalker(float a_elapsedTime)
        {
            Vector2 lastPosition;
            Vector2 newPosition;
            Vector2 EnemySpeed;
            Vector2 EnemyAfterCollidedPos;

            foreach (StalkingChicken a in _stalkers)
            {
                lastPosition = a.GetPosition();
                a.Update(a_elapsedTime, _level);
                newPosition = a.GetPosition();

                _enemyHasCollideWithGround = false;

                EnemySpeed = a.GetSpeed();
                EnemyAfterCollidedPos = Collide(lastPosition, newPosition, a._sizes, ref EnemySpeed, ref _enemyHasCollideWithGround);

                a.SetPosition(EnemyAfterCollidedPos.X, EnemyAfterCollidedPos.Y);
                a.SetSpeed(a.GetSpeed().X, a.GetSpeed().Y);

                //Kollar om stalkern når boxen
                if (a.GetPosition().X > 197)
                {
                    _stalkerHurtBox = true;
                }
            }
        }

        //stoppar timern för att kunna bli skadad
        internal void StopHurtTimer()
        {
            this._hurtTimer = 0;
            this._gotHurt = false;
        }

        //hämtar positionen ifrån spelaren
        internal Microsoft.Xna.Framework.Vector2 GetPlayerPosition()
        {
            return _player.GetPosition();
        }

        //sätter positionen för spelaren
        internal void SetPlayerPosition(Microsoft.Xna.Framework.Vector2 a_playerPosition)
        {
            _player.SetPosition(a_playerPosition.X, a_playerPosition.Y);
        }

        //hämtar spelarens hastighet
        internal float GetPlayerSpeed()
        {
            return _player.GetSpeed().Length();
        }

        //hämtar spelarens liv
        internal int GetPlayerHealth()
        {
            return _player.GetCurrentHealth();
        }

        //hämtar nivån
        internal Level GetLevel()
        {
            return _level;
        }

        //rensar listorna med fiender
        internal void ClearPositions()
        {
            _enemy.Clear();
            _carrot.Clear();
        }

        //hämtar den nuvarande nivån
        internal int GetCurrentLevel()
        {
            return _level.GetCurrentLevel();
        }

        //hämtar positionerna för fienderna och lägger dessa i en lista
        internal List<Vector2> GetEnemyPositions()
        {
            List<Vector2> enemyPositions = new List<Vector2>(_enemy.Count);
            for (int i = 0; i < _enemy.Count; i++)
            {
                enemyPositions.Add(_enemy.ElementAt(i).GetPosition());
            }

            return enemyPositions;
        }

        //hämtar positionerna för fiende-minorna och lägger dessa i en lista
        internal List<Vector2> GetEnemyMinePositions()
        {
            List<Vector2> enemyMinePositions = new List<Vector2>(_enemyMines.Count);

            for (int i = 0; i < _enemyMines.Count; i++)
            {
                enemyMinePositions.Add(_enemyMines.ElementAt(i).GetPosition());
            }

            return enemyMinePositions;
        }

        //hämtar morötternas positioner och placerar dessa i en lista
        internal List<Vector2> GetCarrotPositions()
        {
            List<Vector2> carrotPositions = new List<Vector2>(_carrot.Count);
            for (int i = 0; i < _carrot.Count; i++)
            {
                carrotPositions.Add(_carrot.ElementAt(i).GetPosition());
            }

            return carrotPositions;
        }

        //hämar stalkerns position och placerar dessa i en lista
        internal List<Vector2> GetStalkerPositions()
        {
            List<Vector2> stalkerPositions = new List<Vector2>(_stalkers.Count);

            for (int i = 0; i < _stalkers.Count; i++)
            {
                stalkerPositions.Add(_stalkers.ElementAt(i).GetPosition());
            }
            
            return stalkerPositions;
        }

        /**
         * Kollision
         */
        private Vector2 Collide(Vector2 a_oldPos, Vector2 a_newPos, Vector2 a_size, ref Vector2 a_velocity, ref bool a_outCollideGround)
        {

            //Can we move to the position safely?
            if (_level.IsCollidingAtGround(a_newPos, a_size))
            {
                //if not try X movement
                Vector2 xMove = new Vector2(a_newPos.X, a_oldPos.Y);
                if (_level.IsCollidingAtGround(xMove, a_size) == false)
                {

                    if (a_velocity.Y > 0)
                    {
                        a_outCollideGround = true;
                    }

                    //"Studsen"
                    a_velocity.Y *= -0.0f;
                    //"Glidet"
                    a_velocity.X *= 0.0f;// friction should be time-dependant


                    return xMove;
                }
                else
                {
                    //try Y movement
                    Vector2 yMove = new Vector2(a_oldPos.X, a_newPos.Y);
                    if (_level.IsCollidingAtGround(yMove, a_size) == false)
                    {
                        a_velocity.X = 0;
                        return yMove;
                    }
                }
                //remain at the same position
                return a_oldPos;
            }

            return a_newPos;
        }

        private Vector2 CollideAtTrap(Vector2 a_oldPos, Vector2 a_newPos, Vector2 a_size, ref Vector2 a_velocity, ref bool a_outCollideTrap)
        {
            //Can we move to the position safely?
            if (_level.IsCollidingAtTrap(a_newPos, a_size))
            {
                //if not try X movement
                Vector2 xMove = new Vector2(a_newPos.X, a_oldPos.Y);
                if (_level.IsCollidingAtTrap(xMove, a_size) == false)
                {

                    if (a_velocity.Y > 0)
                    {
                        a_outCollideTrap = true;
                    }

                    //"Studsen"
                    a_velocity.Y *= -0.0f;
                    //"Glidet"
                    a_velocity.X *= 0.0f;// friction should be time-dependant

                    _player.ReduceHealth(100);

                    return xMove;
                }
                else
                {
                    //try Y movement
                    Vector2 yMove = new Vector2(a_oldPos.X, a_newPos.Y);
                    if (_level.IsCollidingAtTrap(yMove, a_size) == false)
                    {
                        a_velocity.X = 0;

                        return yMove;
                    }
                }
                //remain at the same position
                return a_oldPos;
            }


            return a_newPos;
        }

        private Vector2 CollideAtBasket(Vector2 a_oldPos, Vector2 a_newPos, Vector2 a_size, ref Vector2 a_velocity, ref bool a_outCollideBasket)
        {
            //Can we move to the position safely?
            if (_level.IsCollidingAtBasket(a_newPos, a_size))
            {
                //if not try X movement
                Vector2 xMove = new Vector2(a_newPos.X, a_oldPos.Y);
                if (_level.IsCollidingAtBasket(xMove, a_size) == false)
                {

                    if (a_velocity.Y > 0)
                    {
                        a_outCollideBasket = true;
                    }

                    //"Studsen"
                    a_velocity.Y *= -0.0f;
                    //"Glidet"
                    a_velocity.X *= 0.0f;// friction should be time-dependant

                    this._hasCollideWithBasket = true;

                    return xMove;
                }
                else
                {
                    //try Y movement
                    Vector2 yMove = new Vector2(a_oldPos.X, a_newPos.Y);
                    if (_level.IsCollidingAtBasket(yMove, a_size) == false)
                    {
                        a_velocity.X = 0;

                        this._hasCollideWithBasket = true;

                        return yMove;
                    }
                }
                //remain at the same position
                return a_oldPos;
            }

            return a_newPos;
        }

        private bool IsCollidingWithEnemy(Enemy a_enemy, Player a_player)
        {
            Collider playerCollider = new Collider(a_player.GetPosition(), (a_player._sizes.X / 2.0f));
            Collider enemyCollider = new Collider(a_enemy.GetPosition(), (a_enemy._sizes.X / 2.0f));
            if (playerCollider.DoCollide(enemyCollider))
            {
                return true;
            }
            return false;
        }

        private bool IsCollidingWithCarrot(Carrot a_carrot, Player a_player)
        {
            Collider playerCollider = new Collider(a_player.GetPosition(), (a_player._sizes.X / 2.0f));
            Collider carrotCollider = new Collider(a_carrot.GetPosition(), (a_carrot._sizes.X / 2.0f));
            if (playerCollider.DoCollide(carrotCollider))
            {
                return true;
            }
            return false;
        }

        private bool IsCollidingWithMine(EnemyMines a_mine, Player a_player)
        {
            Collider playerCollider = new Collider(a_player.GetPosition(), (a_player._sizes.X / 2.0f));
            Collider carrotCollider = new Collider(a_mine.GetPosition(), (a_mine._sizes.X / 2.0f));
            if (playerCollider.DoCollide(carrotCollider))
            {
                return true;
            }
            return false;
        }

        /**
         * REGLER
         */
        internal bool CanJump()
        {
            //regel som kollar om man får hoppa
            return _hasCollidedWithGround;
        }

        internal bool CanGoRight()
        {
            if (_player.GetPosition().X > Level.g_levelWidth)
            {
                return false;
            }
            return true;
        }

        internal bool CanGoLeft()
        {
            if (_player.GetPosition().X < 0)
            {
                return false;
            }
            return true;
        }

        internal bool HasPlayerDied()
        {
            if (GetPlayerHealth() < 1)
            {
                return true;
            }
            return false;
        }

        internal bool CanPlayerPressEnter()
        {
            return _playerWonLevel;
        }

        internal bool HasPlayerWonLevel()
        {
            return _hasCollideWithBasket;
        }

        internal bool HasPlayerWonGame()
        {
            if (_level.GetCurrentLevel() == 3 &&
                _hasCollideWithBasket == true)
            {
                return true;
            }
            return false;
        }

        internal bool PlayerCollidedWithEnemy()
        {
            return _hasCollideWithEnemy;
        }

        internal bool StalkerHurtBox()
        {
            if (_stalkerHurtBox == true)
            {
                return true;
            }
            return false;
        }

        /**
         * UTFÖRANDE
         */

        internal void DoJump()
        {
            _player.DoJump();
        }

        internal void GoRight()
        {
            _player.GoRight();
        }

        internal void GoLeft()
        {
            _player.GoLeft();
        }

        internal void StopMovement()
        {
            _player.SetSpeed(0, _player.GetSpeed().Y);
        }

        internal bool DidPlayerEatCarrot()
        {
            return _playerAteCarrot;
        }

        internal void StopEatingCarrot()
        {
            _playerAteCarrot = false;
        }

        internal bool DidPlayerJumpOnMine()
        {
            return _jumpedOnMine;
        }

        internal void StopJumpOnMine()
        {
            _jumpedOnMine = false;
        }

        //Funktion för att byta bana
        internal void NextLevel(int a_level)
        {
            _level.NextLevel(a_level);
            _player.SetPosition(5.0f, 0);
        }

        //Funktion för att initiera alla fiender på den aktuella banan
        internal void InitializeLevel()
        {
            //nollställ så man inte har vunnit nivån direkt
            _playerWonLevel = false;

            //Läs in kyckling-fiendernas position
            List<Vector2> enemyPositions = _level.GetEnemyPositions();
            Enemy enemy;
            foreach (Vector2 a in enemyPositions)
            {
                enemy = new Enemy();
                enemy.SetPosition(a.X, a.Y);
                _enemy.Add(enemy);
            }

            //läs in minornas position
            List<Vector2> minePositions = _level.GetEnemyMinesPositions();
            EnemyMines enemyMines;
            foreach (Vector2 b in minePositions)
            {
                enemyMines = new EnemyMines();
                enemyMines.SetPosition(b.X, b.Y);
                _enemyMines.Add(enemyMines);
            }

            //läs in morötternas
            List<Vector2> carrotPositions = _level.GetCarrotPositions();
            Carrot carrot;
            foreach (Vector2 b in carrotPositions)
            {
                carrot = new Carrot();
                carrot.SetPosition(b.X, b.Y);
                _carrot.Add(carrot);
            }

            //läs in stalkers
            List<Vector2> stalkerPositions = _level.GetStalkersPosition();
            StalkingChicken stalker;
            foreach (Vector2 b in stalkerPositions)
            {
                stalker = new StalkingChicken();
                stalker.SetPosition(b.X, b.Y);
                _stalkers.Add(stalker);
            }

        }

    }
}
